package com.frame.project.core.exception;

import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;

/**
 * 定义全局异常拦截
 * @author Administrator
 *
 */
@RestControllerAdvice
public class RestExceptionHandler  {

	//错误重定向的controller层的地址
	public static final String errorUrl = "/errorBody";

	//指定ModelAndView视图参数中的name值，方便统一取
	public static final String attributeName = "restResult";

	private static Logger logger = LogManager.getLogger(RestExceptionHandler.class);



	// 捕捉shiro的异常
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler(ShiroException.class)
	public ModelAndView  handleShiroException(HttpServletResponse resp,HttpServletRequest req,ShiroException e) {
		logger.error(e.getMessage(),e);
		Object errorMassage = req.getAttribute("error"); //取出shiro异常massage
		if(errorMassage !=null) { //返回jwt过滤器异常.(这里只是替换错误Massage而已)
			return getRedirect(RestResult.genErrorResult(CodeEnum.CODE_401.getCode(),errorMassage.toString()));
		}
		return getRedirect(RestResult.genErrorResult(CodeEnum.CODE_401));
	}

	// 捕捉UnauthorizedException
	@ResponseStatus(HttpStatus.UNAUTHORIZED)
	@ExceptionHandler(UnauthorizedException.class)
	public ModelAndView handleUnauthorizedException(HttpServletResponse resp,HttpServletRequest req,UnauthorizedException e) {
		logger.error(e.getMessage(),e);
		return getRedirect(RestResult.genErrorResult(CodeEnum.CODE_403));
	}

	// 根据特定的异常返回指定的 HTTP 状态码400
	@ResponseStatus(value=HttpStatus.BAD_REQUEST)  // 400
	@ExceptionHandler(ConstraintViolationException.class)
	public ModelAndView handleValidationException(ConstraintViolationException e) {
		logger.error(e.getMessage(),e);
		Set<ConstraintViolation<?>> errors = e.getConstraintViolations();
		return getRedirect(RestResult.genErrorResult(CodeEnum.CODE_400, errors.toString()));
	}


	// 通用异常的处理，返回500
	@ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)  // 500
	@ExceptionHandler(Exception.class)
	public ModelAndView handleException(HttpServletResponse resp,HttpServletRequest req,Exception e) {
		logger.error(e.getMessage(),e);
		if(e.getMessage() != null) {
			return getRedirect(RestResult.genErrorResult(CodeEnum.CODE_500, e.getMessage()));
		}
		return getRedirect(RestResult.genErrorResult(CodeEnum.CODE_500));
	}


	/**
	 * 这里重定向到Controller层，是为了更灵活的输出异常结果
	 * 因为这里始终是个切面异常输出，所以是无法改变浏览器中 status状态的
	 * (根据是否是前后端分离，来使用)
	 * 重定向到 RestExceptionController类中输出异常信息
	 * @param restResult
	 * @return
	 */
	public ModelAndView getRedirect(RestResultEntity<?> restResult) {
		ModelAndView model = new ModelAndView(errorUrl);
		model.addObject(attributeName, restResult);
		return model;
	} 

}
